﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
using HalconDotNet;
using Go;

namespace fhs
{
    abstract class AutoAction
    {
        static generator _action;
        static generator _pauseAction;
        static Action<bool> _pauseHandler;
        static bool _PauseNow = false;
        static bool _StopNow = false;
        static bool _singleStep = false;

        static public bool Start(Action completedHandler, Action<bool> pauseHandler)
        {
            if (IsRun() || CalibAction.IsRun() || StepAction.IsRun())
            {
                MessageBox.Show("启动失败");
                return false;
            }
            LogMgr.Info("设备启动");
            _pauseHandler = pauseHandler;
            _action = generator.make(GlobalData.mainStrand, Run, delegate ()
            {
                generator.go(GlobalData.mainStrand, async delegate ()
                {
                    if (null != _pauseAction)
                    {
                        await generator.stop_other(_pauseAction);
                        _pauseAction = null;
                    }
                    await StopAction();
                }, delegate ()
                {
                    _action = null;
                    _pauseHandler = null;
                    _StopNow = false;
                    _PauseNow = false;
                    StateForm.obj.Clear();
                    completedHandler();
                });
            });
            _action.run();
            return true;
        }

        static public void Pause()
        {
            if (!IsRun() || _StopNow || _PauseNow)
            {
                return;
            }
            LogMgr.Info("设备暂停");
            _PauseNow = true;
            generator oldPauseAction = _pauseAction;
            _pauseAction = generator.tgo(GlobalData.mainStrand, async delegate ()
            {
                if (null != oldPauseAction)
                {
                    await generator.wait_other(oldPauseAction);
                }
                await generator.suspend_other(_action);
                await PauseAction();
                _pauseHandler(true);
            }, () => _pauseAction = null);
        }

        static public void Resume()
        {
            if (!IsRun() || _StopNow || !_PauseNow)
            {
                return;
            }
            LogMgr.Info("设备恢复");
            generator oldPauseAction = _pauseAction;
            _pauseAction = generator.tgo(GlobalData.mainStrand, async delegate ()
            {
                if (null != oldPauseAction)
                {
                    await generator.wait_other(oldPauseAction);
                }
                if (_PauseNow)
                {
                    await ResumeAction();
                    await generator.resume_other(_action);
                    _PauseNow = false;
                    _pauseHandler(false);
                }
            }, () => _pauseAction = null);
        }

        static public void Stop()
        {
            if (!IsRun() || _StopNow)
            {
                return;
            }
            LogMgr.Info("设备停止");
            _StopNow = true;
            _action?.stop();
            _action?.resume();
        }

        static public bool IsRun()
        {
            return null != _action;
        }

        static public Task PauseNow()
        {
            Pause();
            return generator.pause_self();
        }

        static public async Task Warning(string info)
        {
            await generator.non_async();
        }

        static public Task Step()
        {
            if (_singleStep)
            {
                return PauseNow();
            }
            return generator.non_async();
        }

        static public void EnableSingleStep(bool enable)
        {
            _singleStep = enable;
        }

        static private async Task WarningBuzzer()
        {
            await generator.non_async();
        }

        static private async Task FlashLight()
        {
            await generator.non_async();
        }

        static private async Task CheckStopBtn()
        {
            await generator.non_async();
        }

        static private async Task CheckPauseBtn()
        {
            await generator.non_async();
        }

        static private async Task PauseAction()
        {
            await generator.non_async();
        }

        static private async Task ResumeAction()
        {
            await generator.non_async();
        }

        static private async Task StopAction()
        {
            await generator.non_async();
        }

        static private async Task Home()
        {
            await generator.non_async();
        }

        static private async Task Init()
        {
            await generator.non_async();
        }

        static private async Task AutoRemoveImage()
        {
            string imageRoot = await generator.send_task(() => VisionBase.ImagePath);
            int maxDay = await generator.send_task(() => VisionBase.ImageDay);
            while (true)
            {
                try
                {
                    string[] imagePaths = await generator.send_service(VisionBase.saveWork.service, () => Directory.GetDirectories(imageRoot));
                    foreach (string imagePath in imagePaths)
                    {
                        DateTime imageTime = await generator.send_service(VisionBase.saveWork.service, () => Directory.GetCreationTime(imagePath));
                        if ((DateTime.Now - imageTime).Days > maxDay)
                        {
                            LogMgr.Info($"删除图片文件夹: {imagePath}");
                            await DirectOpt.RemoveDirectory(VisionBase.saveWork.service, imagePath);
                        }
                    }
                    await generator.sleep(3600 * 1000);
                }
                catch (generator.stop_exception) { throw; }
                catch (System.Exception ec)
                {
                    LogMgr.Error(ec.Message);
                    await generator.sleep(10000);
                }
            }
        }

        static private async Task<short> Read(CmdName.MC name)
        {
            while (true)
            {
                try
                {
                    return (short)await CmdName.mc.Read(CmdName.mcs[name]);
                }
                catch (chan_exception)
                {
                    await WarningMsg("MC通信异常");
                }
            }
        }

        static private async Task<int> Read32(CmdName.MC name)
        {
            while (true)
            {
                try
                {
                    return (int)await CmdName.mc.Read32(CmdName.mcs[name]);
                }
                catch (chan_exception)
                {
                    await WarningMsg("MC通信异常");
                }
            }
        }

        static private async Task Write(CmdName.MC name, int data)
        {
            while (true)
            {
                try
                {
                    await CmdName.mc.Write(CmdName.mcs[name], (ushort)data);
                    return;
                }
                catch (chan_exception)
                {
                    await WarningMsg("MC通信异常");
                }
            }
        }

        static private async Task Write32(CmdName.MC name, int data)
        {
            while (true)
            {
                try
                {
                    await CmdName.mc.Write32(CmdName.mcs[name], (uint)data);
                    return;
                }
                catch (chan_exception)
                {
                    await WarningMsg("MC通信异常");
                }
            }
        }

        static private async Task Wait(CmdName.MC name, int st, int rst)
        {
            while (true)
            {
                try
                {
                    await CmdName.mc.Wait(CmdName.mcs[name], (ushort)st, (ushort)rst);
                    return;
                }
                catch (chan_exception)
                {
                    await WarningMsg("MC通信异常");
                }
            }
        }

        static private async Task Wait(CmdName.MC name, int st)
        {
            while (true)
            {
                try
                {
                    while (st != await CmdName.mc.Read(CmdName.mcs[name]))
                    {
                        await generator.sleep(1);
                    }
                    return;
                }
                catch (chan_exception)
                {
                    await WarningMsg("MC通信异常");
                }
            }
        }

        static private async Task WarningMsg(string msg)
        {
            try
            {
                LogMgr.Error(msg);
                StateForm.obj.Warning(msg);
                await PauseNow();
            }
            finally
            {
                StateForm.obj.Clear();
                StateForm.obj.Run();
            }
        }

        static private MyPoint IntersectionLl(MyPoint a1, MyPoint a2, MyPoint b1, MyPoint b2)
        {
            HTuple row, col, isp;
            HOperatorSet.IntersectionLl(a1.Row, a1.Col, a2.Row, a2.Col, b1.Row, b1.Col, b2.Row, b2.Col, out row, out col, out isp);
            return new MyPoint { Row = row, Col = col };
        }

        static private HTuple CalcOffsetTrans(MyPoint markCenter, double deltaAngle, double offsetX, double offsetY, double offsetU)
        {
            MyPoint offset = FlatCalib.RotatePoint(new MyPoint { }, new MyPoint { X = offsetX, Y = offsetY }, deltaAngle + -offsetU);
            HTuple homMat;
            HOperatorSet.HomMat2dIdentity(out homMat);
            HOperatorSet.HomMat2dRotate(homMat, -offsetU, markCenter.X, markCenter.Y, out homMat);
            HOperatorSet.HomMat2dTranslate(homMat, -offset.X, -offset.Y, out homMat);
            return homMat;
        }

        static private MyPoint TransPoint(MyPoint pt, HTuple homMat)
        {
            HTuple x, y;
            HOperatorSet.AffineTransPoint2d(homMat, pt.X, pt.Y, out x, out y);
            return new MyPoint { X = x, Y = y };
        }

        static private HObject GenLines(MyPoint[] a, MyPoint[] b)
        {
            HObject lineContours;
            HOperatorSet.GenEmptyObj(out lineContours);
            for (int i = 0; i < a.Length; i++)
            {
                HObject line;
                HOperatorSet.GenContourPolygonXld(out line, new HTuple(a[i].Row, b[i].Row), new HTuple(a[i].Col, b[i].Col));
                HOperatorSet.ConcatObj(lineContours, line, out lineContours);
            }
            return lineContours;
        }

        static private void ASaveImage(CameraControl view, HObject image, string name, string path)
        {
            if (VisionBase.saveWork.service.count > 100)
            {
                LogMgr.Error("图片缓存太多");
                return;
            }
            HObject dump;
            HOperatorSet.DumpWindowImage(out dump, view.HalconWindow);
            VisionBase.saveWork.service.post(delegate ()
            {
                try
                {
                    Directory.CreateDirectory(path);
                    HOperatorSet.WriteImage(dump, "png", 0, $"{path}/dump_{name}.png");
                    if (null != image)
                    {
                        HOperatorSet.WriteImage(image, "png", 0, $"{path}/{name}.png");
                    }
                }
                catch (Exception)
                {
                    LogMgr.Error("保存图片失败");
                }
            });
        }

        static private double AbsMax(double a, double b)
        {
            return Math.Abs(a) > Math.Abs(b) ? a : b;
        }

        private struct MarkResult
        {
            public MyPoint mark;
            public MyPoint shuMark;
            public MyPoint hengMark;
        }

        private struct CameraAxis
        {
            public double X1;
            public double X2;
            public double Y;
        }

        static private MyPoint GetAxis(VisionName.Camera camera, CameraAxis cellAxis, CameraAxis blAxis)
        {
            switch (camera)
            {
                case VisionName.Camera.T1Cell:
                case VisionName.Camera.T4Cell:
                    return new MyPoint
                    {
                        X = cellAxis.X2,
                        Y = cellAxis.Y
                    };
                case VisionName.Camera.T2Cell:
                case VisionName.Camera.T3Cell:
                    return new MyPoint
                    {
                        X = cellAxis.X1,
                        Y = cellAxis.Y
                    };
                case VisionName.Camera.T5Bl:
                case VisionName.Camera.T8Bl:
                    return new MyPoint
                    {
                        X = blAxis.X2,
                        Y = blAxis.Y
                    };
                case VisionName.Camera.T6Bl:
                case VisionName.Camera.T7Bl:
                    return new MyPoint
                    {
                        X = blAxis.X1,
                        Y = blAxis.Y
                    };
                default:
                    break;
            }
            return default(MyPoint);
        }

        static private async Task<tuple<bool, MarkResult>> Snap(
            bool mark, CameraControl view, string imagePath, int delay,
            Dictionary<VisionName.Camera, tuple<int, HObject, HTuple>> modelIDs,
            VisionName.Camera camera, Vision_Line.Param lineParam, Vision_Mark.Param markParam)
        {
            if (mark)
            {
                Vision_Mark.Param.Mark param = markParam.VisionConfig[camera];
                tuple<int, HObject, HTuple> modelID;
                if (!modelIDs.TryGetValue(camera, out modelID) || param.matchType != modelID.value1 || param.markImage != modelID.value2 || null == modelID.value3)
                {
                    generator.lock_shield();
                    int matchType = param.matchType;
                    HObject markImage = param.markImage;
                    modelID = modelIDs[camera] = await generator.send_service(VisionBase.genWork.service, delegate ()
                    {
                        try
                        {
                            if (null != modelID.value3)
                            {
                                switch (modelID.value1)
                                {
                                    case 0:
                                        Vision_Mark.ClearShapeModel(modelID.value3);
                                        break;
                                    case 1:
                                        Vision_Mark.ClearNccModel(modelID.value3);
                                        break;
                                }
                            }
                            switch (matchType)
                            {
                                case 0:
                                    return tuple.make(matchType, markImage, Vision_Mark.CreateShapeModel(markImage));
                                case 1:
                                    return tuple.make(matchType, markImage, Vision_Mark.CreateNccModel(markImage));
                                default:
                                    return new tuple<int, HObject, HTuple>(matchType, markImage, null);
                            }
                        }
                        catch (Exception)
                        {
                            LogMgr.Error($"{camera} Mark模板构建失败");
                            return new tuple<int, HObject, HTuple>(matchType, markImage, null);
                        }
                    });
                    await generator.unlock_shield();
                    if (null == modelID.value3)
                    {
                        return tuple.make(false, default(MarkResult));
                    }
                }
                generator.lock_shield();
                Vision_Mark.Result result = (Vision_Mark.Result)await VisionMgr.visions[VisionName.Vision.Mark].Translate(view, null, markParam,
                    new Vision_Mark.Option { camera = camera, modelID = modelID.value3, delay = delay });
                await generator.unlock_shield();
                if (null != result && !result.error)
                {
                    ASaveImage(view, result.image, $"{camera}_OK", imagePath);
                    return tuple.make(true, new MarkResult { mark = result.markEx, hengMark = result.hengMark, shuMark = result.shuMark });
                }
                if (null != result)
                {
                    ASaveImage(view, result.image, $"{camera}_NG", imagePath);
                }
                return tuple.make(false, default(MarkResult));
            }
            else
            {
                generator.lock_shield();
                Vision_Line.Result result = (Vision_Line.Result)await VisionMgr.visions[VisionName.Vision.Line].Translate(view, null, lineParam, new Vision_Line.Option { camera = camera, delay = delay });
                await generator.unlock_shield();
                if (null != result && !result.error)
                {
                    ASaveImage(view, result.image, $"{camera}_OK", imagePath);
                    return tuple.make(true, new MarkResult { mark = result.mark, hengMark = result.hengMark, shuMark = result.shuMark });
                }
                if (null != result)
                {
                    ASaveImage(view, result.image, $"{camera}_NG", imagePath);
                }
                return tuple.make(false, default(MarkResult));
            }
        }

        static private MyPoint[] Inter(
            bool inter, int selectCamera, int selectA, int selectB,
            VisionName.Camera[] cameras, MarkResult[] marks, MyPoint[] axisOffset,
            Dictionary<VisionName.Camera, FlatCalib> camerasCalibRobot,
            Dictionary<VisionName.Camera, MyPoint> camerasCenter)
        {
            MyPoint[] inters = new MyPoint[5];
            if (0 == selectCamera)
            {
                if (inter)
                {
                    MyPoint[] blRobotShuMarks = new MyPoint[4];
                    MyPoint[] blRobotHengMarks = new MyPoint[4];
                    for (int j = 0; j < 4; j++)
                    {
                        blRobotShuMarks[j] = camerasCalibRobot[cameras[j]].Trans(marks[j].shuMark) - camerasCenter[cameras[j]] - axisOffset[j];
                        blRobotHengMarks[j] = camerasCalibRobot[cameras[j]].Trans(marks[j].hengMark) - camerasCenter[cameras[j]] - axisOffset[j];
                    }
                    inters[0] = IntersectionLl(blRobotHengMarks[3], blRobotHengMarks[0], blRobotShuMarks[0], blRobotShuMarks[1]);
                    inters[1] = IntersectionLl(blRobotShuMarks[0], blRobotShuMarks[1], blRobotHengMarks[1], blRobotHengMarks[2]);
                    inters[2] = IntersectionLl(blRobotHengMarks[1], blRobotHengMarks[2], blRobotShuMarks[2], blRobotShuMarks[3]);
                    inters[3] = IntersectionLl(blRobotShuMarks[2], blRobotShuMarks[3], blRobotHengMarks[3], blRobotHengMarks[0]);
                }
                else
                {
                    for (int j = 0; j < 4; j++)
                    {
                        inters[j] = camerasCalibRobot[cameras[j]].Trans(marks[j].mark) - camerasCenter[cameras[j]] - axisOffset[j];
                    }
                }
                inters[4] = IntersectionLl(inters[0], inters[2], inters[1], inters[3]);
            }
            else
            {
                inters[selectA] = camerasCalibRobot[cameras[selectA]].Trans(marks[selectA].mark) - camerasCenter[cameras[selectA]] - axisOffset[selectA];
                inters[selectB] = camerasCalibRobot[cameras[selectB]].Trans(marks[selectB].mark) - camerasCenter[cameras[selectB]] - axisOffset[selectB];
            }
            return inters;
        }

        static private async Task AutoRun(string currProduct, int selectCamera)
        {
            string calibPath = $"./Product/Config/{currProduct}/ProductCalib";
            await generator.send_task(() => Directory.CreateDirectory(calibPath));
            Dictionary<VisionName.Camera, FlatCalib> camerasCalibRobot = new Dictionary<VisionName.Camera, FlatCalib>();
            Dictionary<VisionName.Camera, FlatCalib> camerasCalibAxis = new Dictionary<VisionName.Camera, FlatCalib>();
            Dictionary<VisionName.Camera, tuple<double, double>> camerasSize = new Dictionary<VisionName.Camera, tuple<double, double>>();
            Dictionary<VisionName.Camera, MyPoint> camerasCenter = new Dictionary<VisionName.Camera, MyPoint>();
            Dictionary<VisionName.Camera, MyPoint> camerasCenterRobot = new Dictionary<VisionName.Camera, MyPoint>();
            Dictionary<VisionName.Camera, MyPoint> camerasCenterAxis = new Dictionary<VisionName.Camera, MyPoint>();
            Dictionary<VisionName.Camera, double> camerasCenterFeedIn = new Dictionary<VisionName.Camera, double>();
            bool hasCenter = false, calibExpired = false;
            try
            {
                foreach (var item in CameraMgr.cameras)
                {
                    VisionName.Camera camera = item.Key;
                    string[] center = await generator.send_task(() => File.ReadAllLines($"{calibPath}/{camera}Center.txt"));
                    camerasCenter[camera] = new MyPoint { X = double.Parse(center[0]), Y = double.Parse(center[1]) };
                    string[] centerRobot = await generator.send_task(() => File.ReadAllLines($"{calibPath}/{camera}CenterRobot.txt"));
                    camerasCenterRobot[camera] = new MyPoint { X = double.Parse(centerRobot[0]), Y = double.Parse(centerRobot[1]) };
                    string[] centerAxis = await generator.send_task(() => File.ReadAllLines($"{calibPath}/{camera}CenterAxis.txt"));
                    camerasCenterAxis[camera] = new MyPoint { X = double.Parse(centerAxis[0]), Y = double.Parse(centerAxis[1]) };
                    string[] centerFeedIn = await generator.send_task(() => File.ReadAllLines($"{calibPath}/{camera}CenterFeedIn.txt"));
                    camerasCenterFeedIn[camera] = double.Parse(centerFeedIn[0]);
                }
                hasCenter = true;
                LogMgr.Info("有当前机种旋转标定");
            }
            catch (generator.stop_exception) { throw; }
            catch (Exception)
            {
                LogMgr.Warning("没有当前机种旋转标定");
                camerasCenter.Clear();
                camerasCenterRobot.Clear();
                camerasCenterAxis.Clear();
                camerasCenterFeedIn.Clear();
            }
            try
            {
                DateTime expiredTime = DateTime.Now + TimeSpan.FromDays(3);
                foreach (var item in CameraMgr.cameras)
                {
                    VisionName.Camera camera = item.Key;
                    FlatCalib robotCalib = new FlatCalib();
                    await generator.send_task(() => robotCalib.Load($"./CalibData/{camera}CalibRobot.txt"));
                    camerasCalibRobot[camera] = robotCalib;
                    FlatCalib axisCalib = new FlatCalib();
                    await generator.send_task(() => axisCalib.Load($"./CalibData/{camera}CalibAxis.txt"));
                    camerasCalibAxis[camera] = axisCalib;
                    string[] size = await generator.send_task(() => File.ReadAllLines($"./CalibData/{camera}Size.txt"));
                    camerasSize[camera] = new tuple<double, double>(double.Parse(size[0]), double.Parse(size[1]));
                    if (!hasCenter)
                    {
                        bool isOld = await generator.send_task(() => File.GetLastWriteTime($"./CalibData/{camera}Center.txt") < File.GetLastWriteTime($"./CalibData/{camera}CalibRobot.txt"));
                        if (isOld)
                        {
                            LogMgr.Warning($"{camera}手眼标定之后没有重新旋转标定");
                            if (await generator.send_control(MainForm.obj, () => MessageBox.Show($"{camera}手眼标定之后没有重新旋转标定，是否继续？", "警告", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.Cancel))
                            {
                                return;
                            }
                        }
                        string[] center = await generator.send_task(() => File.ReadAllLines($"./CalibData/{camera}Center.txt"));
                        camerasCenter[camera] = new MyPoint { X = double.Parse(center[0]), Y = double.Parse(center[1]) };
                        string[] centerRobot = await generator.send_task(() => File.ReadAllLines($"./CalibData/{camera}CenterRobot.txt"));
                        camerasCenterRobot[camera] = new MyPoint { X = double.Parse(centerRobot[0]), Y = double.Parse(centerRobot[1]) };
                        string[] centerAxis = await generator.send_task(() => File.ReadAllLines($"./CalibData/{camera}CenterAxis.txt"));
                        camerasCenterAxis[camera] = new MyPoint { X = double.Parse(centerAxis[0]), Y = double.Parse(centerAxis[1]) };
                        string[] centerFeedIn = await generator.send_task(() => File.ReadAllLines($"./CalibData/{camera}CenterFeedIn.txt"));
                        camerasCenterFeedIn[camera] = double.Parse(centerFeedIn[0]);
                    }
                    if (await generator.send_task(() => File.GetLastWriteTime($"./CalibData/{camera}CalibRobot.txt") > expiredTime ||
                        File.GetLastWriteTime($"./CalibData/{camera}CalibAxis.txt") > expiredTime))
                    {
                        calibExpired = true;
                    }
                }
            }
            catch (generator.stop_exception) { throw; }
            catch (Exception ec)
            {
                LogMgr.Error($"标定数据不完整, {ec.Message}");
                await WarningMsg("标定数据不完整，必须先进行相机标定和机种旋转标定");
                return;
            }
            bool rotateNeg = await generator.send_task(() => DefaultLoad.Load(false, "./Config/RotateNeg.txt", 0));
            ProductParam productParam = ProductMgr.products[currProduct];
            VisionName.Camera[] cellCamera = Make.Array(VisionName.Camera.T1Cell, VisionName.Camera.T2Cell, VisionName.Camera.T3Cell, VisionName.Camera.T4Cell);
            VisionName.Camera[] blCamera = Make.Array(VisionName.Camera.T5Bl, VisionName.Camera.T6Bl, VisionName.Camera.T7Bl, VisionName.Camera.T8Bl);
            CameraControl[] cellView = Make.Array(InitOption.T1View, InitOption.T2View, InitOption.T3View, InitOption.T4View);
            CameraControl[] blView = Make.Array(InitOption.T5View, InitOption.T6View, InitOption.T7View, InitOption.T8View);
            bool firstLength = false, lastCompleted = true;
            int selectA = -1, selectB = -1;
            double[] blLengths = null, cellLengths = null, axisAngle = null;
            MyPoint[] blAxisOffset = new MyPoint[blCamera.Length], blInter = null;
            chan<double> blCompleted = chan<double>.make(1);
            generator.children blAction = new generator.children();
            CameraAxis cellAxis = default(CameraAxis), blAxis = default(CameraAxis);
            Vision_Line.Param lineParam = (Vision_Line.Param)VisionMgr.visionParams[VisionName.Vision.Line];
            Vision_Mark.Param markParam = (Vision_Mark.Param)VisionMgr.visionParams[VisionName.Vision.Mark];
            string imageRoot = await generator.send_task(() => VisionBase.ImagePath), imagePath = null;
            Dictionary<VisionName.Camera, tuple<int, HObject, HTuple>> modelIDs = new Dictionary<VisionName.Camera, tuple<int, HObject, HTuple>>();
            generator.self.append_stop_callback(delegate ()
            {
                VisionBase.genWork.service.post(delegate ()
                {
                    foreach (var item in modelIDs)
                    {
                        switch (item.Value.value1)
                        {
                            case 0:
                                Vision_Mark.ClearShapeModel(item.Value.value3);
                                break;
                            case 1:
                                Vision_Mark.ClearNccModel(item.Value.value3);
                                break;
                        }
                    }
                });
            });
            
            #region
            switch (selectCamera)
            {
                case 0: selectA = selectB = -1; break;
                case 1: selectA = 0; selectB = 1; break;
                case 2: selectA = 1; selectB = 2; break;
                case 3: selectA = 2; selectB = 3; break;
                case 4: selectA = 3; selectB = 0; break;
                case 5: selectA = 0; selectB = 2; break;
                case 6: selectA = 1; selectB = 3; break;
                default:
                    await WarningMsg("相机选择错误");
                    return;
            }
            if (0 == selectCamera)
            {
                if (4 != cellCamera.Length)
                {
                    await WarningMsg("相机选择错误");
                    return;
                }
            }
            else if (selectA >= cellCamera.Length || selectB >= cellCamera.Length)
            {
                await WarningMsg("相机选择错误");
                return;
            }
            if (!hasCenter)
            {
                double currBlFeedIn = await Read32(CmdName.MC.BL入料轴位) / GlobalData.轴比例.FeedIn;
                MyPoint currBlRobot = new MyPoint
                {
                    X = await Read32(CmdName.MC.BL组装位X) / GlobalData.轴比例.X,
                    Y = await Read32(CmdName.MC.BL组装位Y) / GlobalData.轴比例.Y
                };
                cellAxis = new CameraAxis
                {
                    X1 = await Read32(CmdName.MC.Cell相机轴组装位X1) / GlobalData.轴比例.CellX1,
                    X2 = await Read32(CmdName.MC.Cell相机轴组装位X2) / GlobalData.轴比例.CellX2,
                    Y = await Read32(CmdName.MC.Cell相机轴组装位Y) / GlobalData.轴比例.CellY
                };
                blAxis = new CameraAxis
                {
                    X1 = await Read32(CmdName.MC.BL相机轴组装位X1) / GlobalData.轴比例.BlX1,
                    X2 = await Read32(CmdName.MC.BL相机轴组装位X2) / GlobalData.轴比例.BlX2,
                    Y = await Read32(CmdName.MC.BL相机轴组装位Y) / GlobalData.轴比例.BlY
                };
                await generator.send_task(delegate ()
                {
                    for (int i = 0; i < blCamera.Length; i++)
                    {
                        blAxisOffset[i] = (currBlRobot - camerasCenterRobot[blCamera[i]]) + camerasCalibRobot[blCamera[i]].Trans(camerasCalibAxis[blCamera[i]].Trans(GetAxis(blCamera[i], cellAxis, blAxis) - camerasCenterAxis[blCamera[i]], true));
                        blAxisOffset[i].Y -= currBlFeedIn - camerasCenterFeedIn[blCamera[i]];
                    }
                });
            }
            await generator.send_task(delegate ()
            {
                MyPoint[] refCoord = new MyPoint[blCamera.Length];
                for (int i = 0; i < blCamera.Length; i++)
                {
                    refCoord[i] = camerasCalibRobot[blCamera[i]].Trans(new MyPoint { Row = 0.5 * camerasSize[blCamera[i]].value2, Col = 0.5 * camerasSize[blCamera[i]].value1 }) - camerasCenter[blCamera[i]] - blAxisOffset[i];
                }
                if (0 == selectCamera)
                {
                    axisAngle = new double[4];
                    for (int i = 0; i < 4; i++)
                    {
                        axisAngle[i] = FlatCalib.VectorAngle(refCoord[i] - refCoord[(i + 1) % 4]);
                        LogMgr.Info($"补偿参照角{i + 1}:{FlatCalib.ToDeg(axisAngle[i]).ToString("0.00")}");
                    }
                }
                else
                {
                    axisAngle = new double[1];
                    axisAngle[0] = FlatCalib.VectorAngle(refCoord[selectA] - refCoord[selectB]);
                    LogMgr.Info($"补偿参照角:{FlatCalib.ToDeg(axisAngle[0]).ToString("0.00")}");
                }
                try
                {
                    string[] blLenStr = File.ReadAllLines($"{calibPath}/BlLengths{selectCamera}.txt");
                    string[] cellLenStr = File.ReadAllLines($"{calibPath}/CellLengths{selectCamera}.txt");
                    blLengths = new double[blLenStr.Length];
                    cellLengths = new double[cellLenStr.Length];
                    for (int i = 0; i < blLenStr.Length; i++)
                    {
                        blLengths[i] = double.Parse(blLenStr[i]);
                    }
                    for (int i = 0; i < cellLengths.Length; i++)
                    {
                        cellLengths[i] = double.Parse(cellLenStr[i]);
                    }
                    if (0 == selectCamera)
                    {
                        if (2 != blLengths.Length || 2 != cellLengths.Length)
                        {
                            firstLength = true;
                            blLengths = cellLengths = null;
                        }
                    }
                    else if (1 != blLengths.Length || 1 != cellLengths.Length)
                    {
                        firstLength = true;
                        blLengths = cellLengths = null;
                    }
                }
                catch (Exception)
                {
                    LogMgr.Info($"首次使用机种 {currProduct}");
                    firstLength = true;
                    blLengths = cellLengths = null;
                }
            });
            await Write(CmdName.MC.Cell拍照, 0);
            await Write(CmdName.MC.Cell反馈, 0);
            await Write(CmdName.MC.Cell超限, 0);
            await Write(CmdName.MC.底板拍照, 0);
            await Write(CmdName.MC.底板反馈, 0);
            await Write(CmdName.MC.底板超限, 0);
            await Write32(CmdName.MC.X补偿, 0);
            await Write32(CmdName.MC.Y补偿, 0);
            await Write32(CmdName.MC.U补偿, 0);
            await Write(CmdName.MC.移动超限, 0);
            #endregion

            Action checkARound = delegate ()
            {
                if (lastCompleted)
                {
                    lastCompleted = false;
                    DateTime productTime = DateTime.Now;
                    imagePath = $"{imageRoot}/{productTime.Year}年{productTime.Month}月{productTime.Day}日/{currProduct}/{productTime.Hour}时{productTime.Minute}分{productTime.Second}秒{productTime.Millisecond}";
                    foreach (var view in blView)
                    {
                        view.Clear(); view.ShowImage();
                    }
                    foreach (var view in cellView)
                    {
                        view.Clear(); view.ShowImage();
                    }
                }
            };
            blAction.go(async delegate ()
            {
                while (true)
                {
                    await Wait(CmdName.MC.底板拍照, 1, 0);
                    checkARound();
                    LogMgr.Info("开始Bl拍照");
                    await generator.chan_clear(blCompleted);
                    generator.children blSnapAction = new generator.children();
                    if (hasCenter)
                    {
                        blSnapAction.go(async delegate ()
                        {
                            MyPoint currRobot = new MyPoint
                            {
                                X = await Read32(CmdName.MC.机械手X) / GlobalData.轴比例.X,
                                Y = await Read32(CmdName.MC.机械手Y) / GlobalData.轴比例.Y
                            };
                            CameraAxis currBlAxis = new CameraAxis
                            {
                                X1 = await Read32(CmdName.MC.BL相机轴X1) / GlobalData.轴比例.BlX1,
                                X2 = await Read32(CmdName.MC.BL相机轴X2) / GlobalData.轴比例.BlX2,
                                Y = await Read32(CmdName.MC.BL相机轴Y) / GlobalData.轴比例.BlY
                            };
                            double currBlFeedIn = await Read32(CmdName.MC.BL入料轴位) / GlobalData.轴比例.FeedIn;
                            foreach (var camera in blCamera)
                            {
                                if ((currRobot - camerasCenterRobot[camera]).Mod > 0.001)
                                {
                                    await WarningMsg("Bl拍照位与旋转标定时有XY移动");
                                    await generator.hold();
                                }
                                if ((GetAxis(camera, default(CameraAxis), currBlAxis) - camerasCenterAxis[camera]).Mod > 0.001)
                                {
                                    await WarningMsg("Bl拍照位与旋转标定时有相机轴移动");
                                    await generator.hold();
                                }
                                if (Math.Abs(currBlFeedIn - camerasCenterFeedIn[camera]) > 0.001)
                                {
                                    await WarningMsg("Bl拍照位与旋转标定时有入料轴移动");
                                    await generator.hold();
                                }
                            }
                        });
                    }
                    foreach (var view in blView)
                    {
                        view.Clear(); view.ShowImage();
                    }
                    bool blHasError = false;
                    MarkResult[] blMarks = new MarkResult[4];
                    for (int i = 0; i < blCamera.Length; i++)
                    {
                        if (0 != selectCamera && i != selectA && i != selectB)
                        {
                            continue;
                        }
                        int cameraIdx = i;
                        blSnapAction.go(async delegate ()
                        {
                            var result = await Snap(productParam.BlMarks[cameraIdx], blView[cameraIdx], imagePath, productParam.BlDelay, modelIDs, blCamera[cameraIdx], lineParam, markParam);
                            if (result.value1)
                            {
                                blMarks[cameraIdx] = result.value2;
                            }
                            else
                            {
                                blHasError = true;
                            }
                        });
                    }
                    await blSnapAction.wait_all();
                    if (!blHasError)
                    {
                        LogMgr.Info("Bl拍照完成");
                        double blLengthErr = 0;
                        await generator.send_task(delegate ()
                        {
                            blInter = Inter(productParam.BlInter, selectCamera, selectA, selectB, blCamera, blMarks, blAxisOffset, camerasCalibRobot, camerasCenter);
                            if (0 == selectCamera)
                            {
                                double[] currBlLengths = new double[2];
                                currBlLengths[0] = (blInter[0] - blInter[2]).Mod;
                                currBlLengths[1] = (blInter[1] - blInter[3]).Mod;
                                if (firstLength)
                                {
                                    blLengths = currBlLengths;
                                }
                                blLengthErr = AbsMax(currBlLengths[0] - blLengths[0], currBlLengths[1] - blLengths[1]);
                                LogMgr.Info($"Bl对角长:{currBlLengths[0].ToString("0.00")},{currBlLengths[1].ToString("0.00")}");
                            }
                            else
                            {
                                double[] currBlLengths = new double[1];
                                currBlLengths[0] = (blInter[selectA] - blInter[selectB]).Mod;
                                if (firstLength)
                                {
                                    blLengths = currBlLengths;
                                }
                                blLengthErr = currBlLengths[0] - blLengths[0];
                                LogMgr.Info($"Bl长:{currBlLengths[0].ToString("0.00")}");
                            }
                            LogMgr.Info($"Bl偏差:{blLengthErr.ToString("0.00")}");
                        });
                        if (Math.Abs(blLengthErr) > productParam.ErrLen)
                        {
                            LogMgr.Error("Bl Mark长误差超限");
                            await Write(CmdName.MC.底板超限, 1);
                        }
                        else
                        {
                            await Write(CmdName.MC.底板反馈, 1);
                            await blCompleted.send(blLengthErr);
                        }
                    }
                    else
                    {
                        LogMgr.Error("Bl拍照出错");
                        await Write(CmdName.MC.底板反馈, 2);
                    }
                }
            });

            while (true)
            {
                cellRestart:
                await Wait(CmdName.MC.Cell拍照, 1, 0);
                checkARound();
                if (0 == productParam.ItCount)
                {
                    await blCompleted.receive();
                    LogMgr.Info("直接组装");
                    DataViewForm.Append(0, 0, 0, 0, 0);
                    await Write32(CmdName.MC.X补偿, 0);
                    await Write32(CmdName.MC.Y补偿, 0);
                    await Write32(CmdName.MC.U补偿, 0);
                    lastCompleted = true; await Write(CmdName.MC.Cell反馈, 3);
                    continue;
                }
                int count = 0;
                double blLengthErr = 0, cellLengthErr = 0, totalPosU = 0;
                MyPoint totalPos = new MyPoint { };
                MyPoint[] cellAxisOffset = new MyPoint[cellCamera.Length];
                while (true)
                {
                    count++;
                    #region
                    int tryCount = 0;
                    MyPoint[] cellInter = null;
                    MarkResult[] cellMarks = new MarkResult[cellCamera.Length];
                    while (true)
                    {
                        tryCount++;
                        LogMgr.Info("开始Cell拍照");
                        generator.children cellSnapAction = new generator.children();
                        if (1 == count && 1 == tryCount)
                        {
                            cellSnapAction.go(async delegate ()
                            {
                                MyPoint currCellRobot = new MyPoint
                                {
                                    X = await Read32(CmdName.MC.机械手X) / GlobalData.轴比例.X,
                                    Y = await Read32(CmdName.MC.机械手Y) / GlobalData.轴比例.Y
                                };
                                if (hasCenter)
                                {
                                    CameraAxis currCellAxis = new CameraAxis
                                    {
                                        X1 = await Read32(CmdName.MC.Cell相机轴X1) / GlobalData.轴比例.CellX1,
                                        X2 = await Read32(CmdName.MC.Cell相机轴X2) / GlobalData.轴比例.CellX2,
                                        Y = await Read32(CmdName.MC.Cell相机轴Y) / GlobalData.轴比例.CellY
                                    };
                                    foreach (var camera in cellCamera)
                                    {
                                        if ((currCellRobot - camerasCenterRobot[camera]).Mod > 0.001)
                                        {
                                            await WarningMsg("Cell拍照位与当前机种旋转标定时有XY移动");
                                            await generator.hold();
                                        }
                                        if ((GetAxis(camera, currCellAxis, default(CameraAxis)) - camerasCenterAxis[camera]).Mod > 0.001)
                                        {
                                            await WarningMsg("Cell拍照位与旋转标定时有相机轴移动");
                                            await generator.hold();
                                        }
                                    }
                                }
                                else
                                {
                                    await generator.send_task(delegate ()
                                    {
                                        for (int i = 0; i < cellCamera.Length; i++)
                                        {
                                            cellAxisOffset[i] = (currCellRobot - camerasCenterRobot[cellCamera[i]]) + camerasCalibRobot[cellCamera[i]].Trans(camerasCalibAxis[cellCamera[i]].Trans(GetAxis(cellCamera[i], cellAxis, blAxis) - camerasCenterAxis[cellCamera[i]], true));
                                        }
                                    });
                                }
                            });
                        }
                        foreach (var view in cellView)
                        {
                            view.Clear(); view.ShowImage();
                        }
                        bool cellHasError = false;
                        for (int i = 0; i < cellCamera.Length; i++)
                        {
                            if (0 != selectCamera && i != selectA && i != selectB)
                            {
                                continue;
                            }
                            int cameraIdx = i;
                            cellSnapAction.go(async delegate ()
                            {
                                var result = await Snap(productParam.CellMarks[cameraIdx], cellView[cameraIdx], imagePath, productParam.CellDelay, modelIDs, cellCamera[cameraIdx], lineParam, markParam);
                                if (result.value1)
                                {
                                    cellMarks[cameraIdx] = result.value2;
                                }
                                else
                                {
                                    cellHasError = true;
                                }
                            });
                        }
                        await cellSnapAction.wait_all();
                        if (!cellHasError)
                        {
                            LogMgr.Info("Cell拍照完成");
                            await generator.send_task(delegate ()
                            {
                                cellInter = Inter(productParam.CellInter, selectCamera, selectA, selectB, cellCamera, cellMarks, cellAxisOffset, camerasCalibRobot, camerasCenter);
                                if (0 == selectCamera)
                                {
                                    double[] currCellLengths = new double[2];
                                    currCellLengths[0] = (cellInter[0] - cellInter[2]).Mod;
                                    currCellLengths[1] = (cellInter[1] - cellInter[3]).Mod;
                                    if (firstLength)
                                    {
                                        cellLengths = currCellLengths;
                                    }
                                    LogMgr.Info($"Cell对角长:{currCellLengths[0].ToString("0.00")},{currCellLengths[1].ToString("0.00")}");
                                    cellLengthErr = AbsMax(currCellLengths[0] - cellLengths[0], currCellLengths[1] - cellLengths[1]);
                                }
                                else
                                {
                                    double[] currCellLengths = new double[1];
                                    currCellLengths[0] = (cellInter[selectA] - cellInter[selectB]).Mod;
                                    if (firstLength)
                                    {
                                        cellLengths = currCellLengths;
                                    }
                                    LogMgr.Info($"Cell长:{currCellLengths[0].ToString("0.00")}");
                                    cellLengthErr = currCellLengths[0] - cellLengths[0];
                                }
                                LogMgr.Info($"Cell偏差:{cellLengthErr.ToString("0.00")}");
                            });
                            if (Math.Abs(cellLengthErr) > productParam.ErrLen)
                            {
                                LogMgr.Error("Cell Mark长误差超限");
                                await Write(CmdName.MC.Cell超限, 1);
                                if (count > 1)
                                {
                                    await blCompleted.try_send(blLengthErr);
                                }
                                goto cellRestart;
                            }
                            break;
                        }
                        else
                        {
                            LogMgr.Error("Cell拍照出错");
                            await Write(CmdName.MC.Cell反馈, 2);
                            await Wait(CmdName.MC.Cell拍照, 1, 0);
                        }
                    }
                    #endregion
                    if (1 == count)
                    {
                        blLengthErr = await blCompleted.receive();
                    }
                    double angle = 0;
                    MyPoint distance = default(MyPoint);
                    LinkedList<ObjectShow>[] drawLines = new LinkedList<ObjectShow>[4];
                    generator.lock_shield();
                    await generator.send_service(VisionBase.genWork.service, delegate ()
                    {
                        MyPoint currRotateCenter = new MyPoint { X = totalPos.X / GlobalData.轴比例.X, Y = totalPos.Y / GlobalData.轴比例.Y };
                        if (0 == selectCamera)
                        {
                            double deltaAngleSum = 0;
                            for (int i = 0; i < 4; i++)
                            {
                                double cellAngle = FlatCalib.VectorAngle(cellInter[i] - cellInter[(i + 1) % 4]);
                                double deltaPhi = FlatCalib.NorPhi(cellAngle - axisAngle[i]);
                                deltaAngleSum += deltaPhi;
                                LogMgr.Info($"补偿参照角倾斜{i + 1}:{FlatCalib.ToDeg(deltaPhi).ToString("0.00")}({FlatCalib.ToDeg(cellAngle).ToString("0.00")})");
                            }
                            double deltaAngle = FlatCalib.NorPhi(deltaAngleSum / 4 + productParam.AxisAngle);
                            LogMgr.Info($"补偿参照角倾斜:{FlatCalib.ToDeg(deltaAngle).ToString("0.00")}({FlatCalib.ToDeg(deltaAngleSum / 4).ToString("0.00")}+{FlatCalib.ToDeg(productParam.AxisAngle).ToString("0.00")})");
                            HTuple offsetTrans = CalcOffsetTrans(cellInter[4], deltaAngle, productParam.OffsetX, productParam.OffsetY, productParam.OffsetU);
                            double angleSum = 0;
                            for (int i = 0; i < 4; i++)
                            {
                                MyPoint blA = blInter[i];
                                MyPoint blB = blInter[(i + 1) % 4];
                                MyPoint cellA = TransPoint(cellInter[i], offsetTrans);
                                MyPoint cellB = TransPoint(cellInter[(i + 1) % 4], offsetTrans);
                                double phi = FlatCalib.NorPhi(FlatCalib.VectorAngle(blA - blB, cellA - cellB));
                                LogMgr.Info($"第{i + 1}边倾斜:{FlatCalib.ToDeg(phi).ToString("0.00")}");
                                angleSum += phi;
                            }
                            angle = angleSum / 4;
                            distance = blInter[4] - FlatCalib.RotatePoint(currRotateCenter, TransPoint(cellInter[4], offsetTrans), angle);
                            #region
                            for (int i = 0; i < 4; i++)
                            {
                                MyPoint[] atBlPoints = new MyPoint[4];
                                MyPoint[] atCellPoints = new MyPoint[4];
                                MyPoint[] atCellOffsetPoints = new MyPoint[4];
                                for (int j = 0; j < 4; j++)
                                {
                                    atBlPoints[j] = camerasCalibRobot[cellCamera[i]].Trans(blInter[j] + camerasCenter[cellCamera[i]] + cellAxisOffset[i], true);
                                    atCellPoints[j] = camerasCalibRobot[cellCamera[i]].Trans(cellInter[j] + camerasCenter[cellCamera[i]] + cellAxisOffset[i], true);
                                    atCellOffsetPoints[j] = camerasCalibRobot[cellCamera[i]].Trans(TransPoint(cellInter[j], offsetTrans) + camerasCenter[cellCamera[i]] + cellAxisOffset[i], true);
                                }
                                drawLines[i] = new LinkedList<ObjectShow>();
                                drawLines[i].AddLast(new ObjectShow
                                {
                                    text = "BlEdge",
                                    color = Color.Blue,
                                    obj = GenLines(Make.Array(atBlPoints[0], atBlPoints[1], atBlPoints[2], atBlPoints[3], atBlPoints[0], atBlPoints[1]),
                                                    Make.Array(atBlPoints[1], atBlPoints[2], atBlPoints[3], atBlPoints[0], atBlPoints[2], atBlPoints[3]))
                                });
                                drawLines[i].AddLast(new ObjectShow
                                {
                                    text = "CellEdge",
                                    color = Color.Lime,
                                    obj = GenLines(Make.Array(atCellPoints[0], atCellPoints[1], atCellPoints[2], atCellPoints[3], atCellPoints[0], atCellPoints[1]),
                                                    Make.Array(atCellPoints[1], atCellPoints[2], atCellPoints[3], atCellPoints[0], atCellPoints[2], atCellPoints[3]))
                                });
                                drawLines[i].AddLast(new ObjectShow
                                {
                                    text = "OffsetEdge",
                                    color = Color.Red,
                                    obj = GenLines(Make.Array(atCellOffsetPoints[0], atCellOffsetPoints[1], atCellOffsetPoints[2], atCellOffsetPoints[3], atCellOffsetPoints[0], atCellOffsetPoints[1]),
                                                    Make.Array(atCellOffsetPoints[1], atCellOffsetPoints[2], atCellOffsetPoints[3], atCellOffsetPoints[0], atCellOffsetPoints[2], atCellOffsetPoints[3]))
                                });
                            }
                            #endregion
                        }
                        else
                        {
                            VisionName.Camera cameraA = cellCamera[selectA], cameraB = cellCamera[selectB];
                            double cellAngle = FlatCalib.VectorAngle(cellInter[selectA] - cellInter[selectB]);
                            double deltaAngle = FlatCalib.NorPhi(cellAngle - axisAngle[0] + productParam.AxisAngle);
                            LogMgr.Info($"补偿参照角倾斜:{FlatCalib.ToDeg(deltaAngle).ToString("0.00")}({FlatCalib.ToDeg(cellAngle - axisAngle[0]).ToString("0.00")}+{FlatCalib.ToDeg(productParam.AxisAngle).ToString("0.00")})");
                            HTuple offsetTrans = CalcOffsetTrans(0.5 * (cellInter[selectA] + cellInter[selectB]), deltaAngle, productParam.OffsetX, productParam.OffsetY, productParam.OffsetU);
                            MyPoint cellOffsetTransA = TransPoint(cellInter[selectA], offsetTrans);
                            MyPoint cellOffsetTransB = TransPoint(cellInter[selectB], offsetTrans);
                            angle = FlatCalib.VectorAngle(blInter[selectA] - blInter[selectB], cellOffsetTransA - cellOffsetTransB);
                            distance = 0.5 * (blInter[selectA] + blInter[selectB]) - FlatCalib.RotatePoint(currRotateCenter, 0.5 * (cellOffsetTransA + cellOffsetTransB), angle);
                            #region
                            drawLines[selectA] = new LinkedList<ObjectShow>();
                            drawLines[selectA].AddLast(new ObjectShow
                            {
                                text = "BlEdge",
                                color = Color.Blue,
                                obj = GenLines(Make.Array(camerasCalibRobot[cameraA].Trans(blInter[selectA] + camerasCenter[cameraA] + cellAxisOffset[selectA], true)),
                                                Make.Array(camerasCalibRobot[cameraA].Trans(blInter[selectB] + camerasCenter[cameraA] + cellAxisOffset[selectA], true)))
                            });
                            drawLines[selectA].AddLast(new ObjectShow
                            {
                                text = "CellEdge",
                                color = Color.Green,
                                obj = GenLines(Make.Array(cellMarks[selectA].mark), Make.Array(camerasCalibRobot[cameraA].Trans(cellInter[selectB] + camerasCenter[cameraA] + cellAxisOffset[selectA], true)))
                            });
                            drawLines[selectA].AddLast(new ObjectShow
                            {
                                text = "OffsetEdge",
                                color = Color.Red,
                                obj = GenLines(Make.Array(camerasCalibRobot[cameraA].Trans(cellOffsetTransA + camerasCenter[cameraA] + cellAxisOffset[selectA], true)),
                                                Make.Array(camerasCalibRobot[cameraA].Trans(cellOffsetTransB + camerasCenter[cameraA] + cellAxisOffset[selectA], true)))
                            });
                            drawLines[selectB] = new LinkedList<ObjectShow>();
                            drawLines[selectB].AddLast(new ObjectShow
                            {
                                text = "BlEdge",
                                color = Color.Blue,
                                obj = GenLines(Make.Array(camerasCalibRobot[cameraB].Trans(blInter[selectB] + camerasCenter[cameraB] + cellAxisOffset[selectB], true)),
                                                Make.Array(camerasCalibRobot[cameraB].Trans(blInter[selectA] + camerasCenter[cameraB] + cellAxisOffset[selectB], true)))
                            });
                            drawLines[selectB].AddLast(new ObjectShow
                            {
                                text = "CellEdge",
                                color = Color.Green,
                                obj = GenLines(Make.Array(cellMarks[selectB].mark), Make.Array(camerasCalibRobot[cameraB].Trans(cellInter[selectA] + camerasCenter[cameraB] + cellAxisOffset[selectB], true)))
                            });
                            drawLines[selectB].AddLast(new ObjectShow
                            {
                                text = "OffsetEdge",
                                color = Color.Red,
                                obj = GenLines(Make.Array(camerasCalibRobot[cameraB].Trans(cellOffsetTransA + camerasCenter[cameraB] + cellAxisOffset[selectB], true)),
                                                Make.Array(camerasCalibRobot[cameraB].Trans(cellOffsetTransB + camerasCenter[cameraB] + cellAxisOffset[selectB], true)))
                            });
                            #endregion
                        }
                    });
                    await generator.unlock_shield();
                    for (int i = 0; i < drawLines.Length; i++)
                    {
                        if (null != drawLines[i])
                        {
                            cellView[i].AddObjects(drawLines[i]); cellView[i].ShowImage();
                            ASaveImage(cellView[i], null, $"{cellCamera[i]}_{count}_Draw", imagePath);
                        }
                    }
                    angle = FlatCalib.NorAngle(FlatCalib.ToDeg(angle));
                    LogMgr.Info($"补偿X:{distance.X.ToString("0.00")}, Y:{distance.Y.ToString("0.00")}, U:{angle.ToString("0.00")}");
                    double anglePos = Math.Round(angle * GlobalData.轴比例.U);
                    MyPoint pos = new MyPoint { X = Math.Round(distance.X * GlobalData.轴比例.X), Y = Math.Round(distance.Y * GlobalData.轴比例.Y) };
                    totalPosU += anglePos; totalPos += pos;
                    double totalU = totalPosU / GlobalData.轴比例.U;
                    MyPoint total = new MyPoint { X = totalPos.X / GlobalData.轴比例.X, Y = totalPos.Y / GlobalData.轴比例.Y };
                    if (Math.Abs(totalU) > productParam.MaxAngle || total.Mod > productParam.MaxDis)
                    {
                        LogMgr.Error($"超限角度{totalU.ToString("0.00")}({productParam.MaxAngle.ToString("0.00")}), 超限距离{total.Mod.ToString("0.00")}({productParam.MaxDis.ToString("0.00")})");
                        lastCompleted = true;
                        await Write(CmdName.MC.移动超限, 1);
                        break;
                    }
                    if (firstLength)
                    {
                        firstLength = false;
                        generator.lock_shield();
                        await generator.send_task(delegate ()
                        {
                            if (0 == selectCamera)
                            {
                                File.WriteAllLines($"{calibPath}/BlLengths{selectCamera}.txt", Make.Array(blLengths[0].ToString(), blLengths[1].ToString()));
                                File.WriteAllLines($"{calibPath}/CellLengths{selectCamera}.txt", Make.Array(cellLengths[0].ToString(), cellLengths[1].ToString()));
                            }
                            else
                            {
                                File.WriteAllLines($"{calibPath}/BlLengths{selectCamera}.txt", Make.Array(blLengths[0].ToString()));
                                File.WriteAllLines($"{calibPath}/CellLengths{selectCamera}.txt", Make.Array(cellLengths[0].ToString()));
                            }
                        });
                        await generator.unlock_shield();
                    }
                    if (count >= productParam.ItCount || (Math.Abs(angle) < productParam.ItErrAng && distance.Mod < productParam.ItErrDis))
                    {
                        LogMgr.Info($"对位完成，次数{count}");
                        DataViewForm.Append(total.X, total.Y, totalU, blLengthErr, cellLengthErr);
                        await Write32(CmdName.MC.X补偿, (int)(totalPos.X * GlobalData.轴比例.X));
                        await Write32(CmdName.MC.Y补偿, (int)(totalPos.Y * GlobalData.轴比例.Y));
                        await Write32(CmdName.MC.U补偿, (int)(rotateNeg ? -anglePos : anglePos));
                        lastCompleted = true; await Write(CmdName.MC.Cell反馈, 3);
                        break;
                    }
                    else
                    {
                        await Write32(CmdName.MC.X补偿, (int)pos.X);
                        await Write32(CmdName.MC.Y补偿, (int)pos.Y);
                        await Write32(CmdName.MC.U补偿, (int)(rotateNeg ? -anglePos : anglePos));
                        await Write(CmdName.MC.Cell反馈, 1);
                        await Wait(CmdName.MC.Cell拍照, 1, 0);
                    }
                }
            }
        }
        
        static private async Task Run()
        {
            foreach (var camera in CameraMgr.cameras)
            {
                if (!camera.Value.OpenOK())
                {
                    await WarningMsg($"找不到{camera.Key}相机");
                    return;
                }
            }
            try
            {
                InitOption.T1View?.Reset();
                InitOption.T2View?.Reset();
                InitOption.T3View?.Reset();
                InitOption.T4View?.Reset();
                InitOption.T5View?.Reset();
                InitOption.T6View?.Reset();
                InitOption.T7View?.Reset();
                InitOption.T8View?.Reset();
                int selectCamera = -1;
                string currProduct = null;
                generator.children runChildren = new generator.children();
                while (true)
                {
                    bool reseted = false;
                    if (0 == await Read(CmdName.MC.复位))
                    {
                        LogMgr.Info("等待设备复位");
                        await Wait(CmdName.MC.复位, 1);
                        LogMgr.Info("设备复位完成");
                        reseted = true;
                    }
                    if (reseted || currProduct != ProductMgr.currProduct || selectCamera != ProductMgr.products[currProduct].SelectCamera)
                    {
                        currProduct = ProductMgr.currProduct;
                        selectCamera = ProductMgr.products[currProduct].SelectCamera;
                        await runChildren.stop();
                        LogMgr.Info($"自动切换到料号 {currProduct}");
                        InitOption.T1View?.Clear(); InitOption.T1View?.ShowImage();
                        InitOption.T2View?.Clear(); InitOption.T2View?.ShowImage();
                        InitOption.T3View?.Clear(); InitOption.T3View?.ShowImage();
                        InitOption.T4View?.Clear(); InitOption.T4View?.ShowImage();
                        InitOption.T5View?.Clear(); InitOption.T5View?.ShowImage();
                        InitOption.T6View?.Clear(); InitOption.T6View?.ShowImage();
                        InitOption.T7View?.Clear(); InitOption.T7View?.ShowImage();
                        InitOption.T8View?.Clear(); InitOption.T8View?.ShowImage();
                        runChildren.go(AutoRemoveImage);
                        runChildren.go(functional.bind(AutoRun, currProduct, selectCamera));
                    }
                    await generator.sleep(100);
                }
            }
            finally
            {
                HomeOptionForm.Reset();
            }
        }
    }
}
